{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Fills;

interface

uses Classes, UIObjects, Graphics, Colours, Colour, XMLHelper;

{$IFDEF DESIGNER}
const
  MODIFY_CAPTION = 'Edit Colours';

  // Property categories and hints for designer
  CAT_FILL = 'Fill';
  HINT_COLOUR = 'defines a flat fill colour';
  HINT_TOP_LEFT = 'defines a colour for top left corner of gradient';
  HINT_TOP_RIGHT = 'defines a colour for top right corner of gradient';
  HINT_BOT_LEFT = 'defines a colour for bottom left corner of gradient';
  HINT_BOT_RIGHT = 'defines a colour for bottom right corner of gradient';
  HINT_COLOURS = 'defines a list of colour positions for the linear fill';
  HINT_DIRECTION = 'controls vertical or horizontal direction for the fill';
{$ENDIF}

type
  TAHMFillDirection = (fdHorizontal, fdVertical);

  TAHMFillColourList = class(TAHMUIStateObjectList);

  TAHMAbstractFill = class(TAHMUIDisplayObject)
  protected
    FShade: TAHMCanvasShade;
  public
    constructor Create(const Name: String); override;
    destructor Destroy; override;
    property Shade: TAHMCanvasShade read FShade;
    procedure Render(Control: TObject; iX, iY, iWidth, iHeight: Single); override;
    procedure Render(Control: TObject; iX, iY, iWidth, iHeight: Single; Clip: Boolean); override;
  end;

  TAHMFillClass = class of TAHMAbstractFill;

  TAHMFill = class(TAHMAbstractFill)
  private
    FColour: TAHMColour;
  protected
    procedure SetColour(Colour: TAHMColour);
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveXMLBody(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
  published
    property Colour: TAHMColour read FColour write SetColour;
  end;

  TAHMGradedFill = class(TAHMAbstractFill)
  private
    FTLColour, FTRColour, FBLColour, FBRColour: TAHMColour;
  protected
    procedure SetTLColour(Colour: TAHMColour);
    procedure SetTRColour(Colour: TAHMColour);
    procedure SetBLColour(Colour: TAHMColour);
    procedure SetBRColour(Colour: TAHMColour);
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveXMLBody(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
  published
    property TLColour: TAHMColour read FTLColour write SetTLColour;
    property TRColour: TAHMColour read FTRColour write SetTRColour;
    property BLColour: TAHMColour read FBLColour write SetBLColour;
    property BRColour: TAHMColour read FBRColour write SetBRColour;
  end;

  TAHMLinearFill = class(TAHMAbstractFill)
  private
    FColours: TAHMFillColourList;
    FDirection: TAHMFillDirection;
  protected
    procedure SetDirection(Direction: TAHMFillDirection);
    procedure ColoursUpdated(Sender: TObject);
    procedure UpdateColours;
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    destructor Destroy; override;
    function ModifyActions: Integer; override;
    function Modify(Action: Integer): Boolean; override;
    function ModifyCaption(Action: Integer): String; override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveXMLBody(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
  published
    property Colours: TAHMFillColourList read FColours;
    property Direction: TAHMFillDirection read FDirection write SetDirection;
  end;

  TAHMFillList = class(TAHMUIObjectList)
  private
  protected
    function ClassFromTag(TagName: String): TAHMUIObjectClass; override;
  public
    constructor Create; override;
    function AddFill(const InheritFrom, NewName: String): TAHMFill;
    function AddGradedFill(const InheritFrom, NewName: String): TAHMGradedFill;
    function GetFill(const Name: String): TAHMAbstractFill;
    procedure PopulateDefaultObjects; override;
    procedure CleardownDefaultObjects; override;
  end;


implementation

uses SysUtils, ApplicationManager, Viewport, BaseControls, Canvas, TagNames,
     {$IFDEF DESIGNER}DesignerStateList,{$ENDIF} XMLTags, ErrorCodes, AHMTypes;


constructor TAHMFillList.Create;
begin
  inherited Create;

  XMLTagName := TAG_FILLS;
  ChildClass := TAHMAbstractFill;
end;

function TAHMFillList.ClassFromTag(TagName: String): TAHMUIObjectClass;
begin
  // Determine appropriate fill to create based on tag
  if      TagName = TAG_FILL       then Result := TAHMFill
  else if TagName = TAG_GRADEDFILL then Result := TAHMGradedFill
  else if TagName = TAG_LINEARFILL then Result := TAHMLinearFill
  else                                  Result := nil;
end;

function TAHMFillList.AddFill(const InheritFrom, NewName: String): TAHMFill;
begin
  Result := TAHMFill(Inherit(InheritFrom, NewName, TAHMFill));
end;

function TAHMFillList.AddGradedFill(const InheritFrom, NewName: String): TAHMGradedFill;
begin
  Result := TAHMGradedFill(Inherit(InheritFrom, NewName, TAHMGradedFill));
end;

function TAHMFillList.GetFill(const Name: String): TAHMAbstractFill;
begin
  Result := TAHMAbstractFill(GetObject(Name));
end;

procedure TAHMFillList.PopulateDefaultObjects;
begin
  // Create default objects as TAHMGradedFill instances
  ChildClass := TAHMGradedFill;
  try
    with DefaultObject as TAHMGradedFill do
    begin
      TLColour := TAHMColour.Create('Left');
      TLColour.ColourDef := '99ffffff';
      TRColour := TAHMColour.Create('Right');
      TRColour.ColourDef := '55ccccb0';
      BLColour := TLColour;
      BRColour := BRColour;
    end;
  finally
    ChildClass := TAHMAbstractFill;
  end;
end;

procedure TAHMFillList.CleardownDefaultObjects;
begin
  with DefaultObject as TAHMGradedFill do
  begin
    TLColour.Free;
    TRColour.Free;
  end;
end;

//----------------------------------------------------------------------------//

constructor TAHMAbstractFill.Create(const Name: String);
begin
  inherited Create(Name);

  ObjectType := otFills;
  ImageIndex := Ord(otFills);
  PreviewFullscreen := True;
  PreviewEditable := False;
end;

destructor TAHMAbstractFill.Destroy;
begin
  FShade.Free;

  inherited;
end;

procedure TAHMAbstractFill.Render(Control: TObject; iX, iY, iWidth, iHeight: Single);
begin
  // Delegate to method below without clipping
  Render(Control, iX, iY, iWidth, iHeight, False);
end;

procedure TAHMAbstractFill.Render(Control: TObject; iX, iY, iWidth, iHeight: Single; Clip: Boolean);
var
  oControl: TAHMAbstractControl;
begin
  // We may get passed a container or control, so use base class
  oControl := Control as TAHMAbstractControl;

  // Draw our fill onto control viewport
  with oControl.Viewport, oControl do // Control properties take precedence
  begin
    if Clip then
      Canvas.FillRect(CanvasRect(iX, iY, iWidth, iHeight), ClipRect, FShade, Gamma, XScale, YScale)
    else
      Canvas.FillRect(CanvasRect(iX, iY, iWidth, iHeight), FShade, Gamma, XScale, YScale);
  end;
end;

//----------------------------------------------------------------------------//

constructor TAHMFill.Create(const Name: String);
begin
  inherited Create(Name);

  FShade := TAHMCanvasShade.Create;
  XMLTagName := TAG_FILL;
end;

procedure TAHMFill.SetColour(Colour: TAHMColour);
begin
  // Keep shade object in sync with colour selection
  FColour := Colour;
  if Assigned(Colour) then FShade.SingleColour := Colour.Colour
                      else FShade.SingleColour := nil;
end;

procedure TAHMFill.ClearProperties;
begin
  inherited;

  // Clear custom fill properties
  SetColour(nil);
end;

procedure TAHMFill.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
      Colour := ColourList.GetColour(DEFAULT_WHITE_NAME);
{$ENDIF}
end;

procedure TAHMFill.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMFill do
    Self.Colour := Colour;
end;

procedure TAHMFill.SaveXMLBody(Lines: TStrings);
var
  oAncs: TAHMFill;
begin
  // Determine which ancestor to compare against - real or empty
  if Ancestor <> nil then oAncs := TAHMFill(Ancestor)
                     else oAncs := TAHMFill.Create('');
  try
    // Only populate attributes that differ from our ancestor
    if (FColour <> nil) and (FColour <> oAncs.Colour) then
      Lines.Add(TABS[3] + FColour.XMLReference);
  finally
    if Ancestor = nil then oAncs.Free;
  end;
end;

procedure TAHMFill.ParseFromXML(XML: TAHMXMLHelper);
var
  sColour: String;
begin
  // Plain fills should just have a colour defined
  if XML.ReadElementAttribute(TAG_COLOUR, ATTR_NAME, sColour) then
    with ApplicationManagerInstance.ThemeManager do
    begin
      if sColour <> '' then
        Colour := VisualDescriptor.ColourList.GetColour(sColour);
    end
  else
    LogParsingError(THM_PRS_VIS_MALFORMED_FILL_TAGS, Self);
end;

procedure TAHMFill.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom fill properties
  Properties.Add('Colour', 'Colour', CAT_FILL, HINT_COLOUR);
{$ENDIF}
end;

//----------------------------------------------------------------------------//
constructor TAHMGradedFill.Create(const Name: String);
begin
  inherited Create(Name);

  FShade := TAHMCanvasGradientShade.Create;
  XMLTagName := TAG_GRADEDFILL;
end;

procedure TAHMGradedFill.SetTLColour(Colour: TAHMColour);
begin
  // Keep shade object in sync with colour selection
  FTLColour := Colour;
  if Assigned(Colour) then FShade.TopLeft := Colour.Colour
                      else FShade.TopLeft := nil;
end;

procedure TAHMGradedFill.SetTRColour(Colour: TAHMColour);
begin
  // Keep shade object in sync with colour selection
  FTRColour := Colour;
  if Assigned(Colour) then FShade.TopRight := Colour.Colour
                      else FShade.TopRight := nil;
end;

procedure TAHMGradedFill.SetBLColour(Colour: TAHMColour);
begin
  // Keep shade object in sync with colour selection
  FBLColour := Colour;
  if Assigned(Colour) then FShade.BottomLeft := Colour.Colour
                      else FShade.BottomLeft := nil;
end;

procedure TAHMGradedFill.SetBRColour(Colour: TAHMColour);
begin
  // Keep shade object in sync with colour selection
  FBRColour := Colour;
  if Assigned(Colour) then FShade.BottomRight := Colour.Colour
                      else FShade.BottomRight := nil;
end;

procedure TAHMGradedFill.ClearProperties;
begin
  inherited;

  // Clear custom gradient fill properties
  SetTLColour(nil);
  SetTRColour(nil);
  SetBLColour(nil);
  SetBRColour(nil);
end;

procedure TAHMGradedFill.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    begin
      TLColour := ColourList.GetColour(DEFAULT_OBJECT_NAME);
      BRColour := ColourList.GetColour(DEFAULT_OBJECT_NAME);
      TRColour := ColourList.GetColour(DEFAULT_WHITE_NAME);
      BLColour := ColourList.GetColour(DEFAULT_WHITE_NAME);
    end;
{$ENDIF}
end;

procedure TAHMGradedFill.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMGradedFill do
  begin
    Self.TLColour := TLColour;
    Self.TRColour := TRColour;
    Self.BLColour := BLColour;
    Self.BRColour := BRColour;
  end;
end;

procedure TAHMGradedFill.SaveXMLBody(Lines: TStrings);
var
  oAncs: TAHMGradedFill;
begin
  // Determine which ancestor to compare against - real or empty
  if Ancestor <> nil then oAncs := TAHMGradedFill(Ancestor)
                     else oAncs := TAHMGradedFill.Create('');
  try
    // Only populate attributes that differ from our ancestor
    if (FTLColour <> nil) and (FTLColour <> oAncs.TLColour) then
      Lines.Add(TABS[3] + TGB+TAG_TOPLEFT+TGC + FTLColour.XMLReference + TGE+TAG_TOPLEFT+TGC);
    if (FTRColour <> nil) and (FTRColour <> oAncs.TRColour) then
      Lines.Add(TABS[3] + TGB+TAG_TOPRIGHT+TGC + FTRColour.XMLReference + TGE+TAG_TOPRIGHT+TGC);
    if (FBLColour <> nil) and (FBLColour <> oAncs.BLColour) then
      Lines.Add(TABS[3] + TGB+TAG_BOTLEFT+TGC + FBLColour.XMLReference + TGE+TAG_BOTLEFT+TGC);
    if (FBRColour <> nil) and (FBRColour <> oAncs.BRColour) then
      Lines.Add(TABS[3] + TGB+TAG_BOTRIGHT+TGC + FBRColour.XMLReference + TGE+TAG_BOTRIGHT+TGC);
  finally
    if Ancestor = nil then oAncs.Free;
  end;
end;

procedure TAHMGradedFill.ParseFromXML(XML: TAHMXMLHelper);
var
  sColTL, sColTR, sColBL, sColBR: String;
  FillNode, ColourNode: TAHMXMLNode;
begin
  // Initialise parsing nodes
  FillNode := XML.CurrentNode;
  ColourNode := nil;

  // Reset graded fill corner colour names
  sColTL := ''; sColTR := ''; sColBL := ''; sColBR := '';

  // Graded fills should have 4 colours defined
  while XML.NextElement(FillNode, ColourNode) do
  begin
    if XML.CurrentElement = TAG_TOPLEFT then
    begin
      if not XML.ReadElementAttribute(TAG_COLOUR, ATTR_NAME, sColTL) then
        LogParsingError(THM_PRS_VIS_MALFORMED_GRADED_FILL_TAGS, Self);
    end
    else if XML.CurrentElement = TAG_TOPRIGHT then
    begin
      if not XML.ReadElementAttribute(TAG_COLOUR, ATTR_NAME, sColTR) then
        LogParsingError(THM_PRS_VIS_MALFORMED_GRADED_FILL_TAGS, Self);
    end
    else if XML.CurrentElement = TAG_BOTLEFT then
    begin
      if not XML.ReadElementAttribute(TAG_COLOUR, ATTR_NAME, sColBL) then
        LogParsingError(THM_PRS_VIS_MALFORMED_GRADED_FILL_TAGS, Self);
    end
    else if XML.CurrentElement = TAG_BOTRIGHT then
    begin
      if not XML.ReadElementAttribute(TAG_COLOUR, ATTR_NAME, sColBR) then
        LogParsingError(THM_PRS_VIS_MALFORMED_GRADED_FILL_TAGS, Self);
    end;
  end;

  // Populate defined corner colour properties
  with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
  begin
    if sColTL <> '' then TLColour := ColourList.GetColour(sColTL);
    if sColTR <> '' then TRColour := ColourList.GetColour(sColTR);
    if sColBL <> '' then BLColour := ColourList.GetColour(sColBL);
    if sColBR <> '' then BRColour := ColourList.GetColour(sColBR);
  end;
end;

procedure TAHMGradedFill.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom fill properties
  Properties.Add('Top Left', 'TLColour', CAT_FILL, HINT_TOP_LEFT);
  Properties.Add('Top Right', 'TRColour', CAT_FILL, HINT_TOP_RIGHT);
  Properties.Add('Bottom Left', 'BLColour', CAT_FILL, HINT_BOT_LEFT);
  Properties.Add('Bottom Right', 'BRColour', CAT_FILL, HINT_BOT_RIGHT);
{$ENDIF}
end;

//----------------------------------------------------------------------------//
constructor TAHMLinearFill.Create(const Name: String);
begin
  inherited Create(Name);

  FShade := TAHMCanvasLinearShade.Create;
  XMLTagName := TAG_LINEARFILL;

  FColours := TAHMFillColourList.Create;
  FColours.ObjectTypes := [otColours];
  FColours.Owner := Self;
  FColours.OnModified := ColoursUpdated;
{$IFDEF DESIGNER}
  FColours.DisplayClassName := 'Linear Fill colour';
{$ENDIF}
end;

destructor TAHMLinearFill.Destroy;
begin
  FColours.Free;

  inherited;
end;

procedure TAHMLinearFill.SetDirection(Direction: TAHMFillDirection);
begin
  FDirection := Direction;

  // Apply new direction to shade instance
  with FShade as TAHMCanvasLinearShade do
    if FDirection = fdHorizontal then Orientation := orHorizontal
                                 else Orientation := orVertical;
end;

procedure TAHMLinearFill.ColoursUpdated(Sender: TObject);
begin
  // Synchronise colours with shade instance
  UpdateColours;
end;

function ColourListComparePositions(List: TStringList; Index1, Index2: Integer): Integer;
begin
  // Compare positions as integers to determine sort order
  Result := StrToIntDef(List[Index1], 0) - StrToIntDef(List[Index2], 0);
end;

procedure TAHMLinearFill.UpdateColours;
var
  i, Position: Integer;
begin
  // Apply colours and orientation to shade instance
  with FShade as TAHMCanvasLinearShade do
  begin
    Clear;

    // If only one colour then just add it at 0 and 100 positions
    if FColours.Count = 1 then
    begin
      Add(0, TAHMColour(FColours.Objects[0]).Colour);
      Add(1, TAHMColour(FColours.Objects[0]).Colour);
    end
    else
    begin
      // Add colours in order of position - sort list first
      FColours.InternalObjects.CustomSort(ColourListComparePositions);

      for i := 0 to Pred(FColours.Count) do
      begin
        Position := StrToIntDef(FColours.States[i], 0);
        if Position < 0 then Position := 0
        else if Position > 100 then Position := 100;

        // If first colour is not at position 0, copy to an extra start colour
        if (i = 0) and (Position <> 0) then
          Add(0, TAHMColour(FColours.Objects[i]).Colour);

        Add(Position / 100, TAHMColour(FColours.Objects[i]).Colour);

        // If last colour is not at position 100, copy to an extra end colour
        if (i = Pred(FColours.Count)) and (Position <> 100) then
          Add(1, TAHMColour(FColours.Objects[i]).Colour);
      end;
    end;

    if FDirection = fdHorizontal then Orientation := orHorizontal
                                 else Orientation := orVertical;
  end;
end;

procedure TAHMLinearFill.ClearProperties;
begin
  inherited;

  // Clear custom linear fill properties
  FColours.Clear;
  FDirection := fdHorizontal;
  UpdateColours;
end;

function TAHMLinearFill.ModifyActions: Integer;
begin
  // Only supports colour editing action
  Result := 1;
end;

function TAHMLinearFill.Modify(Action: Integer): Boolean;
begin
{$IFDEF DESIGNER}
   // Edit colours with list dialog
  Result := TfrmObjectStateList.ShowList(FColours);
  if Result then DoModified;
{$ELSE}
  Result := False;
{$ENDIF}
end;

function TAHMLinearFill.ModifyCaption(Action: Integer): String;
begin
{$IFDEF DESIGNER}
  Result := MODIFY_CAPTION;
{$ELSE}
  Result := '';
{$ENDIF}
end;

procedure TAHMLinearFill.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    begin
      Colours.AddObject('0', ColourList.GetColour(DEFAULT_OBJECT_NAME));
      Colours.AddObject('50', ColourList.GetColour(DEFAULT_WHITE_NAME));
    end;
{$ENDIF}
end;

procedure TAHMLinearFill.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMLinearFill do
  begin
    Self.Colours.Assign(Colours);
    Self.Direction := Direction;
    Self.UpdateColours;
  end;
end;


procedure TAHMLinearFill.SaveXMLBody(Lines: TStrings);
var
  oAncs: TAHMLinearFill;
  sWork: String;
  i: Integer;
begin
  // Determine which ancestor to compare against - real or empty
  if Ancestor <> nil then oAncs := TAHMLinearFill(Ancestor)
                     else oAncs := TAHMLinearFill.Create('');
  try
    // Only populate attributes that differ from our ancestor
    if FColours.IsDifferent(oAncs.Colours) then
    begin
      Lines.Add(TABS[3] + TGB+TAG_COLOURS+TGC);
      for i := 0 to Pred(FColours.Count) do
      begin
        sWork := TGB+TAG_COLOUR+SPC+ATTR_POSITION+ATB + FColours.States[i] + ATN;
        sWork := sWork + ATTR_NAME+ATB + FColours.Objects[i].Name;
        Lines.Add(TABS[4] + sWork + ATE);
      end;
      Lines.Add(TABS[3] + TGE+TAG_COLOURS+TGC);
    end;

    // Do we need to store direction?
    if (FDirection <> fdHorizontal) and (FDirection <> oAncs.Direction) then
    begin
      sWork := TGB+TAG_DIRECTION+SPC+ATTR_VERTICAL+ATB + Bool2Str(FDirection = fdVertical);
      Lines.Add(TABS[3] + sWork + ATE);
    end;
  finally
    if Ancestor = nil then oAncs.Free;
  end;
end;

procedure TAHMLinearFill.ParseFromXML(XML: TAHMXMLHelper);
var
  sPos, sColour, sVertical: String;
  FillNode, PropsNode, ColourNode: TAHMXMLNode;
begin
  // Initialise parsing nodes
  FillNode := XML.CurrentNode;
  PropsNode := nil;

  // Linear fills should have colours and direction defined
  with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    while XML.NextElement(FillNode, PropsNode) do
    begin
      if XML.CurrentElement = TAG_COLOURS then
      begin
        // Handle linear colours
        ColourNode := nil;
        while XML.FindElement(PropsNode, ColourNode, TAG_COLOUR) do
        begin
          sPos := XML.GetAttribute(ATTR_POSITION);
          sColour := XML.GetAttribute(ATTR_NAME);
          FColours.AddObject(sPos, ColourList.GetColour(sColour));
        end;
      end
      else if XML.CurrentElement = TAG_DIRECTION then
      begin
        // Get direction attribute
        sVertical := XML.GetAttribute(ATTR_VERTICAL);
        if sVertical <> '' then
          if Str2Bool(sVertical) then FDirection := fdVertical;
      end;
    end;
end;

procedure TAHMLinearFill.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom fill properties
  Properties.Add('Colours', 'Colours', CAT_FILL, HINT_COLOURS);
  Properties.Add('Direction', 'Direction', CAT_FILL, HINT_DIRECTION);
{$ENDIF}
end;

end.
